package com.android.clinic.app.photo;

import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.text.TextUtils;

import java.io.Closeable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CropUtil {

	private static final String SCHEME_FILE = "file";
	private static final String SCHEME_CONTENT = "content";

	public static void closeSilently(Closeable c) {
		if (c == null)
			return;
		try {
			c.close();
		} catch (Throwable t) {
			// Do nothing
		}
	}

	public static int getExifRotation(File imageFile) {
		if (imageFile == null)
			return 0;
		try {
			ExifInterface exif = new ExifInterface(imageFile.getAbsolutePath());
			// We only recognize a subset of orientation tag values
			switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
			case ExifInterface.ORIENTATION_ROTATE_90:
				return 90;
			case ExifInterface.ORIENTATION_ROTATE_180:
				return 180;
			case ExifInterface.ORIENTATION_ROTATE_270:
				return 270;
			default:
				return ExifInterface.ORIENTATION_UNDEFINED;
			}
		} catch (IOException e) { 
			return 0;
		}
	}

	public static boolean copyExifRotation(File sourceFile, File destFile) {
		if (sourceFile == null || destFile == null)
			return false;
		try {
			ExifInterface exifSource = new ExifInterface(sourceFile.getAbsolutePath());
			ExifInterface exifDest = new ExifInterface(destFile.getAbsolutePath());
			exifDest.setAttribute(ExifInterface.TAG_ORIENTATION,
					exifSource.getAttribute(ExifInterface.TAG_ORIENTATION));
			exifDest.saveAttributes();
			return true;
		} catch (IOException e) { 
			return false;
		}
	}

	public static File getFromMediaUri(Context context, ContentResolver resolver, Uri uri) {
		if (uri == null)
			return null;

		if (SCHEME_FILE.equals(uri.getScheme())) {
			return new File(uri.getPath());
		} else if (SCHEME_CONTENT.equals(uri.getScheme())) {
			final String[] filePathColumn = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME };
			Cursor cursor = null;
			try {
				cursor = resolver.query(uri, filePathColumn, null, null, null);
				if (cursor != null && cursor.moveToFirst()) {
					final int columnIndex = (uri.toString().startsWith("content://com.google.android.gallery3d"))
							? cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
							: cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
					// Picasa images on API 13+
					if (columnIndex != -1) {
						String filePath = cursor.getString(columnIndex);
						if (!TextUtils.isEmpty(filePath)) {
							return new File(filePath);
						}
					}
				}
			} catch (IllegalArgumentException e) {
				// Google Drive images
				return getFromMediaUriPfd(context, resolver, uri);
			} catch (SecurityException ignored) {
				// Nothing we can do
			} finally {
				if (cursor != null)
					cursor.close();
			}
		}
		return null;
	}

	private static String getTempFilename(Context context) throws IOException {
		File outputDir = context.getCacheDir();
		File outputFile = File.createTempFile("image", "tmp", outputDir);
		return outputFile.getAbsolutePath();
	}

	private static File getFromMediaUriPfd(Context context, ContentResolver resolver, Uri uri) {
		if (uri == null)
			return null;

		FileInputStream input = null;
		FileOutputStream output = null;
		try {
			ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r");
			FileDescriptor fd = pfd.getFileDescriptor();
			input = new FileInputStream(fd);

			String tempFilename = getTempFilename(context);
			output = new FileOutputStream(tempFilename);

			int read;
			byte[] bytes = new byte[4096];
			while ((read = input.read(bytes)) != -1) {
				output.write(bytes, 0, read);
			}
			return new File(tempFilename);
		} catch (IOException ignored) {
			// Nothing we can do
		} finally {
			closeSilently(input);
			closeSilently(output);
		}
		return null;
	}

	public static void startBackgroundJob(MonitoredActivity activity, String title, String message, Runnable job,
                                          Handler handler) {
		// Make the progress dialog uncancelable, so that we can guarantee
		// the thread will be done before the activity getting destroyed
		ProgressDialog dialog = ProgressDialog.show(activity, title, message, true, false);
		new Thread(new BackgroundJob(activity, job, dialog, handler)).start();
	}

	private static class BackgroundJob extends MonitoredActivity.LifeCycleAdapter implements Runnable {

		private final MonitoredActivity activity;
		private final ProgressDialog dialog;
		private final Runnable job;
		private final Handler handler;
		private final Runnable cleanupRunner = new Runnable() {
			public void run() {
				activity.removeLifeCycleListener(BackgroundJob.this);
				if (dialog.getWindow() != null)
					dialog.dismiss();
			}
		};

		public BackgroundJob(MonitoredActivity activity, Runnable job, ProgressDialog dialog, Handler handler) {
			this.activity = activity;
			this.dialog = dialog;
			this.job = job;
			this.activity.addLifeCycleListener(this);
			this.handler = handler;
		}

		public void run() {
			try {
				job.run();
			} finally {
				handler.post(cleanupRunner);
			}
		}

		@Override
		public void onActivityDestroyed(MonitoredActivity activity) {
			// We get here only when the onDestroyed being called before
			// the cleanupRunner. So, run it now and remove it from the queue
			cleanupRunner.run();
			handler.removeCallbacks(cleanupRunner);
		}

		@Override
		public void onActivityStopped(MonitoredActivity activity) {
			dialog.hide();
		}

		@Override
		public void onActivityStarted(MonitoredActivity activity) {
			dialog.show();
		}
	}

}
